package util;

import java.awt.Component;
import java.awt.Container;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Window;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JOptionPane;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;

public class AwtUtils {
    public static interface ComponentPredicate {
        boolean matches(Component c);
    }

    /**
     * Finds all components that match predicate within active window.
     * 
     * @param componentPredicate
     * @return Set of components that match predicate
     * @author Mykhaylo Adamovych
     */
    public static Set<Component> findComponents(ComponentPredicate componentPredicate) {
        return findComponents(componentPredicate, false);
    }

    /**
     * Finds all components that match predicate.
     * 
     * @param componentPredicate
     * @param searchInactiveWindows
     * @return Set of components that match predicate
     * @author Mykhaylo Adamovych
     */
    public static Set<Component> findComponents(ComponentPredicate componentPredicate, boolean searchInactiveWindows) {
        Set<Component> result = new HashSet<Component>();
        if (searchInactiveWindows)
            for (Window window : getVisibleWindows()) {
                if (componentPredicate.matches(window))
                    result.add(window);
                findComponents(window, componentPredicate, result);
            }
        else {
            Window activeWindow = getActiveWindow();
            if (activeWindow != null) {
                if (componentPredicate.matches(activeWindow))
                    result.add(activeWindow);
                findComponents(activeWindow, componentPredicate, result);
            }
        }
        return result;
    }

    private static void findComponents(Container container, ComponentPredicate componentPredicate, Set<Component> result) {
        if (container == null)
            return;
        Component[] components = container.getComponents();
        for (Component component : components) {
            if (componentPredicate.matches(component))
                result.add(component);
            if (component instanceof Container)
                findComponents((Container) component, componentPredicate, result);
        }
    }

    /**
     * Finds active window.
     * 
     * @author Mykhaylo Adamovych
     */
    public static Window getActiveWindow() {
        for (Window window : getVisibleWindows())
            if (window.isActive())
                return window;
        return null;
    }

    /**
     * Locates the visible child component that contains current mouse location within active window.
     * 
     * @see Component java.awt.Container.findComponentAt(Point p)
     * @author Mykhaylo Adamovych
     */
    public static Component getComponentUnderCursor() {
        Window activeWindow = getActiveWindow();
        if (activeWindow == null)
            return null;
        Point p = MouseInfo.getPointerInfo().getLocation();
        SwingUtilities.convertPointFromScreen(p, activeWindow);
        return activeWindow.findComponentAt(p);
    }

    /**
     * @param component
     * @return root pane container of the component
     * @author Mykhaylo Adamovych
     */
    public static RootPaneContainer getRootPaneContainer(Component component) {
        Component rootPaneContainer = component;
        while (rootPaneContainer != null) {
            if (rootPaneContainer instanceof RootPaneContainer)
                return (RootPaneContainer) rootPaneContainer;
            rootPaneContainer = rootPaneContainer.getParent();
        }
        return null;
    }

    /**
     * @return visible windows except {@link SwingUtilities.SharedOwnerFrame}
     * @author Mykhaylo Adamovych
     */
    public static Set<Window> getVisibleWindows() {
        Set<Window> result = new HashSet<Window>();
        Window[] windows = Window.getWindows();
        for (Window window : windows)
            if (window.isShowing() && window != JOptionPane.getRootFrame())
                result.add(window);
        return result;
    }

    private AwtUtils() {
    }
}

